﻿#region License
// (c) Intergen.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
#endregion

using System;
using System.Reflection;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Media.Animation;

namespace TextGlow.Control.Utilities
{
  public static class WpfUtils
  {
    public static T Clone<T>(T source)
    {
      return Clone<T>(source, null);
    }

    public static T Clone<T>(T source, Dictionary<Type, InstantiationHandler> typeInstantiationHandlers)
    {
      return Clone<T>(source, typeInstantiationHandlers, null);
    }

    public static T Clone<T>(T source, Dictionary<Type, InstantiationHandler> typeInstantiationHandlers, List<PropertyInfo> ignoredProperties)
    {
      if (source == null)
        throw new ArgumentNullException("source");

      ignoredProperties = ignoredProperties ?? new List<PropertyInfo>();
      typeInstantiationHandlers = typeInstantiationHandlers ?? new Dictionary<Type, InstantiationHandler>();
      Type sourceType = source.GetType();

      T cloned;
      InstantiationHandler handler;
      if (typeInstantiationHandlers.TryGetValue(sourceType, out handler))
        cloned = (T)handler(source);
      else
        cloned = (T)Activator.CreateInstance(sourceType);

      foreach (PropertyInfo curPropInfo in sourceType.GetProperties())
      {
        if (curPropInfo.GetGetMethod() != null
            && (curPropInfo.GetSetMethod() != null))
        {
          if (!ignoredProperties.Contains(curPropInfo))
          {
            // Handle Non-indexer properties
            if (curPropInfo.Name != "Item")
            {
              // get property from source
              object getValue = curPropInfo.GetGetMethod().Invoke(source, new object[] { });
              object clonedValue = curPropInfo.GetGetMethod().Invoke(cloned, new object[] { });

              if (getValue != clonedValue)
              {
                // clone if needed
                if (getValue != null && getValue is DependencyObject)
                  getValue = Clone((DependencyObject)getValue, typeInstantiationHandlers, ignoredProperties);

                // set property on cloned
                curPropInfo.GetSetMethod().Invoke(cloned, new object[] { getValue });
              }
            }
            // handle indexer
            else
            {
              // get count for indexer
              int numberofItemInColleciton =
                  (int)
                  curPropInfo.ReflectedType.GetProperty("Count").GetGetMethod().Invoke(source, new object[] { });

              // run on indexer
              for (int i = 0; i < numberofItemInColleciton; i++)
              {
                // get item through Indexer
                object getValue = curPropInfo.GetGetMethod().Invoke(source, new object[] { i });

                // clone if needed
                if (getValue != null && getValue is DependencyObject)
                  getValue = Clone((DependencyObject)getValue, typeInstantiationHandlers);
                // add item to collection
                curPropInfo.ReflectedType.GetMethod("Add").Invoke(cloned, new object[] { getValue });
              }
            }
          }
        }
      }

      return cloned;
    }

    //public static void BeginWait(Storyboard sb)
    //{
    //  if (sb == null)
    //    throw new ArgumentNullException("sb");

    //  sb.Completed 
    //}
  }
}